/*
 * @Author: hx 2542123711@QQ.com
 * @Date: 2024-11-18 16:02:48
 * @LastEditors: hx 2542123711@QQ.com
 * @LastEditTime: 2024-11-19 20:46:49
 * @FilePath: /class_process-communication/ProcessPoll/ProcessPoll.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include<iostream>
#include<unistd.h>
#include<string>
#include<vector>
#include<cassert>
#include<sys/stat.h>
#include<sys/wait.h>
#include<ctime>
#include"TaskList.hpp"

#define N 2
using namespace std;

int ProcessNum = 5;
vector<task> tasks;

//对管道先描述，管道的读写属性，子进程的pid，子进程的名字
class channel
{
    public:
    channel(int cmdfd, pid_t slaverid, const string& processname)
    :_cmdfd(cmdfd)
    ,_slaverid(slaverid)
    ,_processname(processname)
    {}

    int _cmdfd;//控制向哪个子进程写入的文件描述符
    pid_t _slaverid;//子进程的pid
    string _processname;//子进程的名字
};

//约定好每次写4Byte的数据，读也是读4Byte的数据
void slaver()
{
    int cmdcode = 0;
    //cout << "read rid:" << getpid() << " rfd:" << rfd << endl;
    //我们发现我们每一个进程的文件描述符都是3，每次father关闭读端口，也就是把3关闭，下次再Pipe创建文件读端仍然是3，所以会被子进程继承下去
    //父进程的写端是4 5 6，子进程是读端3
    //子进程在管道读取父进程发送的任务码，从而接下来去执行任务
    while(true)
    {
        int n = read(0, &cmdcode, sizeof(int));
        if(n == sizeof(int)) //如果父进程不发送数据，子进程就阻塞等待
        {
            //执行任务码cmdcode对应的任务
            if(cmdcode > 0 && cmdcode <=tasks.size())
            {
                tasks[cmdcode-1]();
            }
            // cout << "slaver say@ get a command: " << getpid() << " cmdcode: " << cmdcode <<endl;
        }

        if(n == 0)
        {
            break;
        }
    }
}

//输入参数 const &
//输出参数 *
//输入输出 &
void InitProcessPoll(vector<channel>* channels)
{
    for(int i = 1; i <= ProcessNum; i++)
    {
        int pipefd[N]={0};
        int n = pipe(pipefd);
        assert(!n);
        (void) n;

        pid_t id = fork();
        if(id < 0)
        {
            return;
        }
        else if(id == 0)
        {   
            //child
            close(pipefd[1]);
            //子进程关闭写端口，所以子进程的写端口为
            //子进程执行
            dup2(pipefd[0], 0);
            close(pipefd[0]);
            slaver();   
            
            cout << "process quit" << getpid() << endl;
            exit(0);
        }
        //father
        //父进程的fork返回子进程的id
        close(pipefd[0]);

        //dup2(pipefd[1], 1);
        string processname = "processname" + to_string(i);
        channels->push_back(channel(pipefd[1], id, processname));
    }
}

void Debug(const vector<channel>& channels)
{
    for(const auto& e : channels)
    {
        cout << "processname:" << e._processname << " mypid:" << e._slaverid <<endl;
    }
}
void Meus()
{
    cout <<"###############################################" <<endl;
    cout <<"#####1.更新日志 2.释放q技能######################" <<endl;
    cout <<"#####3.释放大招 4.更新血量和蓝量##################" <<endl;
    cout <<"###############################################" <<endl;
}

void CtrlSlaver(vector<channel>& channels)
{
    int which = 0;
    int cnt = 5;
    while(cnt)
    {
        //1.选择任务码
        int task_code = rand()%(tasks.size()+1);
        //用户手动选择任务
        // Meus();
        // int task_code = 0;
        // cin >> task_code;
        // if(task_code < 0 || task_code > tasks.size()+1)
        // {
        //     break;
        // }

        //2.选择进程
        // int processpos = rand()%channels.size(); //随机选值
        int processpos = which;
        cout << "father say: " << "task_code: "  << task_code <<"  already send to" <<channels[processpos]._slaverid 
        << " processname: " << channels[processpos]._processname << endl;

        //3.发送任务
        write(channels[processpos]._cmdfd, &task_code, sizeof(int));
        which++;
        which %= channels.size();

        cnt--;
        sleep(1);
    }
}

void QuitProcess(vector<channel>& channels)
{
    for(const auto& e :channels)
    {
        close(e._cmdfd);
    }
    //这个时候写端关闭，读端读到0就可以退出来

    //回收子进程
    int status = 0;
    for(int i=0; i<ProcessNum; i++)
    {
        pid_t rid = waitpid(channels[i]._slaverid, &status, 0);
        if(rid < 0)
        {
            cout << channels[i]._processname << " wait failed" << endl;
        }
    }  
}

int main()
{
    InitTask(&tasks);
    srand(time(nullptr)^getpid()^1023);//种下一个随机数的种子
    
    //再组织，用vecotr把channel组织起来 -达到管理的功能
    vector<channel> channels;
    //1.初始化创建3个子进程，把子进程的信息放到vector里面进行管理，让他们帮我们完成任务
    InitProcessPoll(&channels);
    //Debug(channels);

    //2.开始控制子进程 -父进程要通过管道向子进程发送任务码，父进程可以选择某个编号的子进程，给他发送任务码
    CtrlSlaver(channels);
    
    //3.清理工作
    QuitProcess(channels);
    
    return 0;
}